/*
 * directoryEntry.c
 *
 *  Created on: 26/10/2011
 *      Author: utn_so
 */

#include "utils.h"
#include "directoryEntry.h"
#include "bootsecto.h"
#include "lista.h"
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>

extern 		char fat_table_array[524288];

void printEntry(directoryEntry entry){

	printf("el nombre es, %s\n",entry.name);
	printf("el atributo es, %d\n",entry.attribute);
	printf("el tiempo de creacion (fino) es, %d\n",entry.createTimeFine);
	printf("el tiempo de creacion es, %d y %d\n",entry.createTime[0],entry.createTime[1]);
	printf("la fecha de creacion es, %d y %d\n",entry.createDate[0],entry.createDate[1]);
	printf("la ultima fecha de acceso es, %d y %d\n",entry.lastAccessDate[0],entry.lastAccessDate[1]);
	printf("el contenido de la entrada comienza en el cluster: %d\n",entry.firstCluster);
	printf("la ultima hora de modificacion es, %d y %d\n",entry.lastModifiedTime[0],entry.lastModifiedTime[1]);
	printf("la ultima fecha de modificacion es, %d y %d\n",entry.lastModifiedDate[0],entry.lastModifiedDate[1]);
	printf("el size del archivo es:%d bytes \n",entry.fileSize);
	puts("\n");
}


size_t conversion_8to16(char *utf8,char*utf16){

	size_t size_utf8=strlen(utf8),size_utf16=0;
	int rta=0;

	rta= unicode_utf8_to_utf16_inbuffer(utf8,size_utf8,utf16,&size_utf16);

	if(rta==1){
		//printf("utf16:%s\n",utf16);
		return (size_utf16*2)+1;
	}
	else{
		printf("error en pasaje\n");
		return 0;
	}
}

size_t conversion_16to8(char*utf16,char*utf8){

	size_t size_utf8=0,size_utf16=14;
	int rta=0;

	rta= unicode_utf16_to_utf8_inbuffer(utf16,size_utf16,utf8,&size_utf8);

	if(rta==1){
		return strlen(utf8);
	}
	else{
		printf("error en pasaje\n");
		return strlen(utf8);
	}
}

void readLongEntry(char *map, unsigned int offset,char **pname){
	int i;
	char *ret,entry[27];
	unsigned char orden,length,j;
	static char utf8[14];

	orden= map[0+offset];

	for (i=1;i<=10;i++){

			entry[i-1] =map[i+ offset];
		}
	for (i=14;i<=25;i++){

			entry[i-4] =map[i+ offset];
		}
	for (i=28;i<=31;i++){

			entry[i-6] =map[i+ offset];
		}


	if ( orden > 64){
		length = conversion_16to8(entry,utf8);
		*pname = (char*)malloc(sizeof(char)*length);
		strcpy(*pname,utf8);
	}
	else {
		entry[26]='\0';

		ret = (char*)malloc(length*sizeof(char));
		strcpy(ret,*pname);

		j= conversion_16to8(entry,utf8);
		length = length+j;
		free(*pname);
		*pname = (char*)calloc(length,sizeof(char)*length);
		strcpy(*pname,utf8);

		strcat(*pname,ret);
		free(ret);
	}
	//printf("nombre largo:%s\n",*pname);
}

void readShortEntry(char *map, unsigned int offset,directoryEntry *entry){

	memset(entry->extension,0,3);


	entry->attribute = map[11 +offset];

	if (entry->attribute!=16){
		entry->extension[0] = map[8 +offset];
		entry->extension[1] = map[9 +offset];
		entry->extension[2] = map[10 +offset];
	}

	entry->createTimeFine = map[13 +offset];
	entry->createTime[0] = map[14 +offset];
	entry->createTime[1] = map[15 +offset];

	entry->createDate[0] = map[16 +offset];
	entry->createDate[1] = map[17 +offset];

	entry->lastAccessDate[0] = map[18 +offset];
	entry->lastAccessDate[1] = map[19 +offset];

	entry->lastModifiedTime[0] = map[22 +offset];
	entry->lastModifiedTime[1] = map[23 +offset];

	entry->lastModifiedDate[0] = map[24 +offset];
	entry->lastModifiedDate[1] = map[25+offset];

	char highlow[4]={map[21 +offset],map[20 +offset],map[27+offset],map[26 +offset]};

	entry->firstCluster=concat_uint(highlow,4);

	char size[4]={map[31+offset],map[30+offset],map[29+offset],map[28+offset]};

	entry->fileSize=concat_uint(size,4);

}
//lee un cluster de una entrada de directorio
//y compara el nombre con cada una de las entradas
void processEntry(char *map,directoryEntry* entry,unsigned int offset,unsigned char *flag){

	int j=0;
	char auxname[9];

			if(map[offset]!=46){
				if( map[11 +offset] == 0x0F){ //atributo es LFN

					readLongEntry(map,offset,&entry->name);
					*flag=0;
				}
				else{
					readShortEntry(map,offset,entry);

					if (*flag==1){
						memset(auxname,0,8);
						while(j<8 && map[j+offset]!=32){

							auxname[j]=map[j+offset];
							j++;
						}
						j=0;
						entry->name=malloc(8*sizeof(char));
						strcpy(entry->name,auxname);

					}

					//printEntry(*entry);

					*flag=1;
				}
			}
}


//dado el path de un dir o arch (ej:/carpeta) agrega a una lista de offset de entradas libres (lst- offset1->offset2->...)
//retorna -1 si lo enc, 0 o 2 si no lo enc

int read_free_entry(char disk,uNodo **lst,const char *path,unsigned int rootclus,unsigned int *offset){

	unsigned int i=0,j=1,offset_root=0x104000,cont=1,indice=0;
	int enc=1;
	char**newpath,*map;
	unsigned char flag=1;
	newpath=NULL;
	directoryEntry entry;
	newpath = string_split2(path,'/');

	if((strcmp(path, "/") == 0)){
		newpath[1]=NULL;
	}

	while (newpath[cont]!=NULL){
		cont++;
	}

	if(*offset==0){
		*offset=offset_root;
	}

	while (cont>1 && enc==1){

		map = mmap(0,1,PROT_READ ,MAP_PRIVATE, disk,*offset);
		enc=-1;

		while(i < 128 && enc!=1){

			if(map[indice]==0 || map[indice]==-27){   			 //si estan libres

				printf("offset+indice:%d\n",*offset+indice);
				add_uint(lst,*offset+indice);						//agrego offset a la lista de entradas libres

			}
			else{

				processEntry(map,&entry,indice,&flag);

				if (flag==1){										//si creo la entrada corta
					enc=strcmp(entry.name,newpath[j])+1; 		//comparo los nombres, si coinciden (enc=1) encontro la entrada
				}

			}
		indice+=32;
		i++;
		}

		if(cont!=1){
			*offset=4096*((entry.firstCluster)-rootclus)+offset_root;
			printf("offset:%d\n",*offset);
		}
		//verificar en fat si ocupa mas de 1 cluster

		j++;
		cont--;
	}

	return enc;
}
//dado el path de un dir (ej:/carpeta) agrega a una lista sus entradas (lst- hola hello.txt..)
//retorna -1 si lo enc, 0 o 2 si no lo enc

int read_directory(char disk,NodoEntry **lst,const char *path,unsigned int rootclus,unsigned int *offset){

	unsigned int i=0,j=1,offset_root=0x104000,cont=1,indice=0;
	int enc=1;
	char**newpath,*map;
	unsigned char flag=1;
	newpath=NULL;
	directoryEntry entry;
	newpath = string_split2(path,'/');

	if((strcmp(path, "/") == 0)){
		newpath[1]=NULL;
	}

	while (newpath[cont]!=NULL){
		cont++;
	}

	if(*offset==0){
		*offset=offset_root;
	}

	while (cont>=1 && enc==1){

		map = mmap(0,1,PROT_READ ,MAP_PRIVATE, disk,*offset);
		enc=-1;

		while(i < 128 && enc!=1){

			if ((map[indice]!=0)&&(map[indice]!=-27)){

				processEntry(map,&entry,indice,&flag);

				if (flag==1){										//si creo la entrada corta

					if(cont==1){   									//si termino el path
						add_entry(lst,entry);						//es el directorio que hay que listar, agrego entry a la lista
					}
					else{
						enc=strcmp(entry.name,newpath[j])+1; 		//comparo los nombres, si coinciden (enc=1) encontro la entrada
					}
				}
			}

		indice+=32;
		i++;
		}

		if(cont!=1){
			*offset=4096*((entry.firstCluster)-rootclus)+offset_root;
			printf("offset:%d\n",*offset);
		}
		//verificar en fat si ocupa mas de 1 cluster

		j++;
		cont--;
	}

	return enc;
}

//dado un path (ej:/carpeta) lee su entrada(arch o dir) y la devuelve (entry- nombre:carpeta atributos:..)
//retorna 1 si encontro la entrada, 0 o 2 si no lo enc

int read_entry(char disk,const char *path,unsigned int rootclus,directoryEntry *entry,unsigned int *offset){
	unsigned int i=0,indice=0,j=1,offset_root=0x104000,cont=1;
	unsigned char flag=1;
	int enc=1,*fat_table;
	char**newpath,*map;
	newpath=NULL;

	newpath = string_split2(path,'/');

	while (newpath[cont]!=NULL){
		cont++;
	}
	//fat_table = getFatTable(disk);
	if(*offset==0){
		*offset=offset_root;
	}

	while (cont>1 && enc==1){

		map = mmap(0,1,PROT_READ ,MAP_PRIVATE, disk,*offset);
		enc=-1;

		while(i < 128 && enc!=1){

			if ((map[indice]!=0)&&(map[indice]!=-27)){

				processEntry(map,entry,indice,&flag);

				if (flag==1){										//si creo la entrada corta
					enc=strcmp(entry->name,newpath[j])+1; 		//comparo los nombres, si coinciden (enc=1) encontro la entrada
				}
			}

		indice+=32;
		i++;
		}

		*offset=4096*((entry->firstCluster)-rootclus)+offset_root;
		printf("offset:%d\n",*offset);

		j++;
		cont--;
	}

	return enc;
}

//lee el cluster numCluster de un path
//retorna el contenido de numCluster (direccion al siguiente cluster), 0 en caso de error
Nodo* clusterDeUnArchivo(char disk,const char*path,unsigned int numCluster){
	directoryEntry entry;
	Nodo *lstCluster=NULL;
	int enc=0,ret=0,*fat_table=NULL;
	unsigned int offset=0;

	if (strcmp(path,"/")!=0){
		enc = read_entry(disk,path,2,&entry,&offset);
	}
	else
	{
		enc=1;
		entry.firstCluster=2;//rootCluster
	}

	if (enc==1){
		fat_table = (int*)fat_table_array;
		ret =entry.firstCluster;
		add_int(&lstCluster,ret);

		while(ret!=268435455){
			ret = fat_table[ret];
			add_int(&lstCluster,ret);
		}

	}
	else{
		puts("no existe el path");
	}

	return lstCluster;
}


void createShortEntry(directoryEntry* shortEntry,unsigned int firstCluster,char attribute,char *extension){

	shortEntry->attribute=attribute;
	strcpy(shortEntry->extension,extension);
	shortEntry->fileSize=0;
	shortEntry->firstCluster=firstCluster;
	memset(shortEntry->createDate,0,2);
	memset(shortEntry->createTime,0,2);
	shortEntry->createTimeFine=0;
	memset(shortEntry->lastAccessDate,0,2);
	memset(shortEntry->lastModifiedDate,0,2);
	memset(shortEntry->lastModifiedTime,0,2);
}


//crea las n entradas para newpath[ultimaEntrada] pasando el name a utf16
unsigned int createNameEntry(char *nameToCreate,longEntry **longEntries,directoryEntry *shortEntry){
	//unsigned int sizeName_utf8,sizeName_utf16;
	unsigned int nroEntries,i,indice=0,j=0,length=0,k;
	char partName[13],auxName[11],auxlong[26];

	shortEntry->name=malloc(8*sizeof(char));

	memset(auxName,0,11);

	for(i=0;i<8;i++){
		auxName[i]=nameToCreate[i];
		auxName[i]=toupper(auxName[i]);
	}


	memcpy(shortEntry->name,auxName,8);

	length = strlen(nameToCreate);
	nroEntries = (length/13)+1;

	struct longEntry longEntries_array[nroEntries];

	memset(auxlong,0,26);

	*longEntries=longEntries_array;

	for (i=1;i<=nroEntries;i++){

		memset(partName,0,13);
		indice=0;

		while(j<13*i){
			partName[indice]=nameToCreate[j];
			j++;
			indice++;
		}

		conversion_8to16(partName,auxlong);

		for (k=0;k<10;k++){
			(longEntries_array[i-1]).name1[k]=auxlong[k];
		}
		for (k=0;k<12;k++){
			(longEntries_array[i-1]).name2[k]=auxlong[k+10];
		}
		for (k=0;k<4;k++){
			(longEntries_array[i-1]).name3[k]=auxlong[k+22];
		}

		if(nroEntries==i){
			longEntries_array[i-1].orden=64+i;
		}
		else{
			longEntries_array[i-1].orden=i;
		}

		longEntries_array[i-1].attribute=15;
		longEntries_array[i-1].firstClusLo=0;
		longEntries_array[i-1].type=0;
		longEntries_array[i-1].chksum=0;

		memset(auxlong,0,26);
	}


	return nroEntries;
}
